/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package newpeer;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.swing.JOptionPane;
import net.jxta.socket.JxtaSocket;

/**
 *
 * @author Administrador
 */
public class ThreadDescargaArchivo extends Thread {

    private String nameArchivo; //El nombre del archivo que este en marcha en ese momento
    private int ipaquete;  // Paquete en el cual se encuentra el archivo a descargar
    private int totalPaquetes;  // Número total de paquetes para calcular el estado de la descarga
    private Peer padre;
    private String idPipe;
    private String tamanoArchivo;
    private String mensajeNew;
    private String NamePeer;
    private Prog barra;

    public String getIdPipe() {
        return idPipe;
    }

    public int getIpaquete() {
        return ipaquete;
    }

    public String getMensajeNew() {
        return mensajeNew;
    }

    public String getNameArchivo() {
        return nameArchivo;
    }

    public String getTamanoArchivo() {
        return tamanoArchivo;
    }

    public int getTotalPaquetes() {
        return totalPaquetes;
    }

    public ThreadDescargaArchivo(String nombreArchivo, String tamanoArchivo, String idPipe, Peer padre, String NamePeer) {
        this.nameArchivo = nombreArchivo;
        this.tamanoArchivo = tamanoArchivo;
        this.idPipe = idPipe;
        this.padre = padre;
        this.NamePeer = NamePeer;
        totalPaquetes = 0;
    }

    @Override
    public void run() {
        //  String mensaje = "DESCARGAR_ARCHIVO" + SEPARADOR_MENSAJE + nombreArchivo;
        ipaquete = 0;
        File ArchivoEstado = new File("C:/temp/Recuperar/" + NamePeer + "/" + nameArchivo + ".txt");
        if (!ArchivoEstado.getParentFile().exists()) {
            ArchivoEstado.getParentFile().mkdirs();
        }
        //nameArchivo = nombreArchivo;
        try {
            JxtaSocket socketServidor = padre.conectarse(idPipe);
            OutputStream clienteOut = socketServidor.getOutputStream();
            InputStream clienteIn = socketServidor.getInputStream();
            PrintWriter pw = new PrintWriter(clienteOut, true);
            pw.println("DESCARGAR_ARCHIVO;" + nameArchivo + ";" + NamePeer);

            long tamArchivo = Long.parseLong(tamanoArchivo);
            byte[] mens = new byte[(int) Peer.TAM_PAQUETES];

            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(clienteIn));
                String mnsPeerIn = br.readLine();
                if (mnsPeerIn.startsWith("PIPE OCUPADO")) {
                    JOptionPane.showMessageDialog(padre, "SORRY PIPE...OCUPADO");
                } else if (mnsPeerIn.startsWith("OK")) {
                    //Recibir la llave del servidor para verificar integridad
                    ObjectInputStream ois = new ObjectInputStream(clienteIn);
                    SecretKey llave = (SecretKey) ois.readObject();
                    ObjectOutputStream oosArchivo = new ObjectOutputStream(new FileOutputStream(new File("c:/temp/llave.obj")));
                    oosArchivo.writeObject(llave);
                    oosArchivo.close();
                    Mac mac2 = Mac.getInstance("HmacMD5");
                    mac2.init(llave);
                    JOptionPane.showMessageDialog(padre, "Llave Secret recibida");
                    ObjectInputStream okeypub = new ObjectInputStream(clienteIn);
                    PublicKey llavepublica = (PublicKey) okeypub.readObject();
                    JOptionPane.showMessageDialog(padre, "Llave publica recibida");
                    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                    Signature signature = Signature.getInstance("SHA1withRSA", "BC");
                    signature.initVerify(llavepublica);

                    File nuevoArchivo = new File(padre.RUTA_ARCHIVOS_COMPARTIDOS_DESCARGADOS + nameArchivo);
                    if (!nuevoArchivo.getParentFile().exists()) {
                        //Si no existen las carpetas crearlas
                        nuevoArchivo.getParentFile().mkdirs();
                    }
                    if (nuevoArchivo.exists()) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmm_ss_SSS");
                        Date fechaActual = new Date();
                        nuevoArchivo = new File(padre.RUTA_ARCHIVOS_COMPARTIDOS_DESCARGADOS + sdf.format(fechaActual) + "-" + nameArchivo);
                    }

                    FileOutputStream fos = new FileOutputStream(nuevoArchivo);
                    long tamPaquetes = 468;
                    long numeroPaquetes = tamArchivo / tamPaquetes;
                    long tamencrip = Peer.TAM_PAQUETES / 4; //paquetes de 128
                    totalPaquetes = (int) numeroPaquetes;
                    barra = new Prog(padre, totalPaquetes, nameArchivo, nuevoArchivo.getName(), ArchivoEstado);
                    long modulo = tamArchivo % tamPaquetes;
                    if (tamArchivo < tamPaquetes) {
                        byte[] menor = new byte[(int) tamArchivo];

                        clienteIn.read(menor);

                        fos.write(menor);
                    } else {


                        int j = 0;
                        for (int i = 0; i < numeroPaquetes; i++) {
                            Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                            rsaCipher.init(Cipher.DECRYPT_MODE, llavepublica);
                            System.out.println("Numero de Paquete: " + numeroPaquetes + "Va en el paquete: " + i);
                            byte[] paq = new byte[(int) Peer.TAM_PAQUETES];
                            clienteIn.read(paq);
                            byte[] encodtemp = new byte[(int) tamencrip];
                            //Leer el diges
                            byte[] digestServidor = new byte[16], digestCalculado;
                            clienteIn.read(digestServidor);
                            digestCalculado = mac2.doFinal(paq);
                            byte[] firma=new byte[128];
                            clienteIn.read(firma);
                            signature.update(paq);
                            System.out.println();
                            //VErificacion de firma
                            if (signature.verify(firma)) {
                                System.out.println("IDENTIFICADO_TRUE");

                            } else {
                                System.out.println("PEER_NO_IDENTIFICADO");
                                int res=JOptionPane.showConfirmDialog(padre, "Los datos actuales no provienen de una fuente identificada ¿Desea continuar?","Aviso de suplantación de indentidad en la Red", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE);
                                if(res==JOptionPane.NO_OPTION){
                                    return;
                                }
                            }

                            //VErificacion de digest
                            if (!verificar(digestServidor, digestCalculado)) {
                                System.out.println("ALERTA DE INTRUSOOOS");
                                int res=JOptionPane.showConfirmDialog(padre, "Los datos han sido cambiados en su curso en la red\nEsto se puede dar porque su red está siendo espiada.\n¿Desea continuar?","Aviso de cambio de datos en la Red", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE);
                                if(res==JOptionPane.NO_OPTION){
                                    return;
                                }
                            }
                            //Desencriptar los 4 paquetes
                            for (int z = 0; z < 4; z++) {
                                for (int h = z * (int) tamencrip, cont = 0; h < (z + 1) * (int) tamencrip; h++, cont++) {
                                    encodtemp[cont] = paq[h];
                                }
                                byte[] decodedBytes = rsaCipher.doFinal(encodtemp);
                                fos.write(decodedBytes);
                                System.out.println("tamaño: " + decodedBytes.length);
                            }

                            DataOutputStream outdata = new DataOutputStream(new FileOutputStream(ArchivoEstado, false));
                            outdata.write((NamePeer + ";" + nameArchivo + ";" + nuevoArchivo.getName() + ";" + tamanoArchivo + ";" + i + ";" + idPipe).getBytes());
                            outdata.close();
                            //fos.write(paq);
                            ipaquete = i;
                            System.out.println("Va en el paquete: " + i);
                            if ((ipaquete == numeroPaquetes - 1) && (modulo == 0)) {
                                mensajeNew = "ESTADO: ARCHIVO DESCARGADO";
                                ArchivoEstado.delete();
                            }
                            barra.iterar(j++);
                        }
                        long loQueFalta = tamArchivo - (numeroPaquetes * 468);
                        if (loQueFalta > 0) {
                            byte[] paq = new byte[(int) loQueFalta];
                            clienteIn.read(paq);
                            fos.write(paq);
                            mensajeNew = "ESTADO: ARCHIVO DESCARGADO";
                            ArchivoEstado.delete();
                            j = j + 1;
                            barra.iterar(j);

                        }
                    }

                    fos.close();
                    padre.desconectarse(socketServidor);
                    JOptionPane.showMessageDialog(padre, "El archivo '" + nameArchivo + "' ha sido descargado exitosamente", "Carga Archivo", JOptionPane.INFORMATION_MESSAGE);
                    padre.getPanelArchivos().abrirArchivo(nuevoArchivo.getAbsolutePath());
                    //Actualizar Interfaz Gráfica
                    padre.getPanelArchivos().actualizarArchivos();
                    //republicar los archivos
                    padre.repaint();
                    padre.pack();
                    padre.compartirArchivoEnRed(nuevoArchivo);
                    padre.sacarDeListaDeDescarga(this);
                }
            } catch (SignatureException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchProviderException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (NoSuchPaddingException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvalidKeyException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalBlockSizeException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (BadPaddingException ex) {
                Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (FileNotFoundException e) {
                JOptionPane.showMessageDialog(padre, "Archivo no encontrado", "Carga Archivo", JOptionPane.ERROR_MESSAGE);
                e.printStackTrace();
                System.out.println("No lo encuentra: " + ipaquete);
                //desconectarDeServidor();
            }

        } catch (ClassNotFoundException ex) {
            System.err.println("Error al recibir la llave del servidor");
            Logger.getLogger(ThreadDescargaArchivo.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            System.err.println("ERROR Peer Caído!!" + "\n" + ex.getMessage());
            System.out.println("Se ha caído el peer que mandaba el archivo, quedó en paquete: " + ipaquete);
        }
    }

    /**
     *
     * @param dig1 digest que se verificará
     * @param digestCalculado digest calculado
     * @return
     */
    public boolean verificar(byte[] digestServidor, byte[] digestCalculado) {
        for (int i = 0; i < digestServidor.length - 1; i++) {
            if (digestServidor[i] != digestCalculado[i]) {
                return false;
            }
        }
        return true;
    }
}
